-
-
Notifications
You must be signed in to change notification settings - Fork 735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding availableKeys to ParseObject.State #596
Conversation
@natario1 updated the pull request - view changes |
Thanks. Do you know how the iOS version handles this issue?
…On Sat, Mar 11, 2017, 9:22 AM Mattia ***@***.***> wrote:
-
Adds a safeKeys property to ParseObject.state that includes keys that
can be safely accessed. Fixes #595
<#595> so now
there is consistency between complete and non-complete objects.
While it seems to work on my machine, I have no deep understanding of
the library. Nothing should be broken though.
-
Exposing ParseObject.isDataAvailable(String key). Right now there are:
ParseObject.has(String key): alias to containsKey(key)
ParseObject.containsKey(String key): true if the server sent out data
for this key
Neither of these can be used to know if ParseObject.get* methods can
be called safely. In some cases has(key) returns false but get(key)
can be called without throwing. In some others has(key) returns false
and get(key) will throw.
This PR exposes isDataAvailable(String key) for partially fetched
objects. When false, get* methods will throw. So with the PR:
ParseObject.has(String key): we have some data for this key from the
server.
ParseObject.containsKey(String key): same.
ParseObject.isDataAvailable(): the object is complete, all getters can be
called.
ParseObject.isDataAvailable(String key): the object might not be
complete, but you can call get(key) safely.
This makes sense to me.
------------------------------
You can view, comment on, or merge this pull request online at:
#596
Commit Summary
- Add safeKeys that can be safely accessed. Exposing
isDataAvailable(key)
File Changes
- *M* Parse/src/main/java/com/parse/NetworkQueryController.java
<https://github.com/ParsePlatform/Parse-SDK-Android/pull/596/files#diff-0>
(2)
- *M* Parse/src/main/java/com/parse/ParseObject.java
<https://github.com/ParsePlatform/Parse-SDK-Android/pull/596/files#diff-1>
(39)
Patch Links:
- https://github.com/ParsePlatform/Parse-SDK-Android/pull/596.patch
- https://github.com/ParsePlatform/Parse-SDK-Android/pull/596.diff
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#596>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAT8yWCWnGFMtnutVzSrsQT8kks6mniKks5rkthpgaJpZM4MaRF1>
.
|
@rogerhu I don't know. As far as I know the JS sdk simply does not throw, so you can call get with any key, and if not present, you get undefined. |
@natario1 updated the pull request - view changes |
What's the behavior on the iOS SDK? Should remove the throwing altogether? |
So iOS Parse has selectKeys() and findObjects() as part of PFQuery and PFQueryState Is selectedKeys() the equivalent of safeKeys? (https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/blob/7a820b75c6726808475af9ce705053ff0e8cbd11/Tests/Unit/QueryStateUnitTests.m) |
@natario1 updated the pull request - view changes |
@rogerhu we also have selectedKeys() as part of ParseQuery.State and that’s used to understand if the object should be considered ‘complete’. So if you select some keys all the objects will be incomplete. The issue is that when objects are incomplete and you call get* on a legit key, ParseObject will throw. The PR adds safeKeys to ParseObject.State , meaning, ‘keys we don’t currently have in our data, but you should be able to call get() on without crashing the app’. This is dirty and I would be OK with @flovilmart with simply removing the throwing. |
Where do we need to remove IllegalStateException throwing? I think that makes more sense too to keep consistent with the other SDKs. |
I have found how iOS handles this issue, exactly here. There’s a This is practically the same to this PR (except that here the |
Should we call it availableKeys instead then? |
Does it deal with dotted notation as well? |
@natario1 updated the pull request - view changes |
Yes, I can refactor to give the same meaning it has in the iOS SDK. It’s a bit risky though, because in that case we must ensure that the I don’t fully understand that part (server data is held in two different maps both in Yes, this implements dotted notation. |
I'm more curious if there's anywhere in the iOS code that deals with dotted notation issues as well... |
@natario1 updated the pull request - view changes |
JSONArray nestedKeys = new JSONArray(); | ||
for (int i = 0; i < selectedKeys.length(); i++) { | ||
String nestedKey = selectedKeys.getString(i); | ||
if (nestedKey.startsWith(key+".")) nestedKeys.put(nestedKey.substring(key.length()+1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happena if this is multiple nested keys? A.b.c.d.e?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first (942-954) the current object will be given its own direct keys (without dots, e.g. b).
Later (958+) we extract the child object subkeys (e.g. b.c.d.e). That is passed to decoder.decode(), which will pass back the JSON object to this method. It will get ‘b’ as a safe key, and will pass ‘c.d.e’... and so on. It goes this way with nested iterations, until there are no nested ParseObjects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great thanks for the explanation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a test for this part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added two in ParseDecoderTest, I will add another one for the a.b situation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks also the key + ”.” could be spaced out
@@ -237,7 +238,16 @@ public void testGetUnavailable() { | |||
ParseObject.State state = mock(ParseObject.State.class); | |||
when(state.className()).thenReturn("TestObject"); | |||
when(state.isComplete()).thenReturn(false); | |||
ParseObject object = ParseObject.from(state); | |||
object.get("foo"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just an assertion that no exception will be raised?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this checks that the exception is raised (see the @test annotation). Actually I have not changed this method, I have added testGetAvailableIfKeySafe() a few lines below, that tests safeKeys() .
* @param json | ||
* The object's data. | ||
* @param defaultClassName | ||
* The className of the object, if none is in the JSON. | ||
* @param isComplete | ||
* {@code true} if this is all of the data on the server for the object. | ||
* @param decoder | ||
* Delegate for knowing how to decode the values in the JSON. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this line deleted accidentally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good catch.
@natario1 updated the pull request - view changes |
for (int i = 0; i < results.length(); ++i) { | ||
JSONObject data = results.getJSONObject(i); | ||
T object = ParseObject.fromJSON(data, resultClassName, state.selectedKeys() == null); | ||
if (isSubset) data.put(ParseObject.KEY_SELECTED_KEYS, selectedKeys); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you pass in selectedKeys into a method instead that accepts selectedKeys() instead?
Similar to:
It seems weird that the NetworkQueryController has access to this private key..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rogerhu I get your point, I tried working directly with a Set, but for that we would need to change the signatures of ParseObject.fromJSON, ParseObject.mergeFromServer, and even worse ParseDecoder.decode, these are called from anywhere and an extra set of "selected keys" rarely makes sense.
The key is also used by ParseDecoder to get keys from the JSON...
Maybe we could add two static methods to ParseObject, void insertSelectedKeys(JSONObject data, Set<String>)
, used by NetworkQueryController, and boolean hasSelectedKeys(JSONObject data)
, used by ParseDecoder. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or I’ll just add another ParseObject.fromJSON that deals with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep :)
@@ -122,7 +122,7 @@ public Object decode(Object object) { | |||
} | |||
|
|||
if (typeString.equals("Object")) { | |||
return ParseObject.fromJSON(jsonObject, null, true, this); | |||
return ParseObject.fromJSON(jsonObject, null, !jsonObject.has(ParseObject.KEY_SELECTED_KEYS), this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems to me that fromJSON() should figure out whether things are complete instead of here right?
Just trying to get KEY_SELECTED_KEYS out of being a public static variable..
Thanks for working on this bug/issue..quite complicated to fix and appreciate the effort. Most of my feedback are minor issues in general. |
@natario1 updated the pull request - view changes |
@@ -98,6 +99,7 @@ private static ParseObjectSubclassingController getSubclassingController() { | |||
private long createdAt = -1; | |||
private long updatedAt = -1; | |||
private boolean isComplete; | |||
private Set<String> safeKeys = new HashSet<>(); | |||
/* package */ Map<String, Object> serverData = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd put a comment here that these are the keys returned from the server but the object may not have defined...Or whatever best way to phrase to explain the intention. It took me all day to get my ahead around this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha yes. Actually the server does not return these, we have them because of query.selectKeys(), but you got the point. Annoying bug.
if (nestedKeys.length() > 0) { | ||
((JSONObject) value).put(KEY_SELECTED_KEYS, nestedKeys); | ||
} | ||
} | ||
Object decodedObject = decoder.decode(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having to pass selected keys as metadata does it help to modify the signature in any way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well we need a fromJSON signature that does not take selectedKeys() .. because ParseDecoder will just have a JSON and is not aware of the selected keys bundled in the JSON (I made KEY_SELECTED_KEYS private as you said), see.
From this point on, bundling the keys in the JSON allows us to not change all the other signatures, and abstract this internal stuff from ParseDecoder. It is also used in Local data store encoding/decoding, see ParseObject.toRest() / fromREST()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha forgot about local store too
@natario1 updated the pull request - view changes |
Lgtm thanks |
Can we rename those to availableKeys? |
@natario1 how easy would it be to change the construct to be availableKeys? The main thing I think is this function here: https://github.com/natario1/Parse-SDK-Android/blob/fcf24e3af94f0321d80d399c418a325e7e3da90f/Parse/src/main/java/com/parse/ParseObject.java#L168-L174 Mostly I think safeKeys() is implemented like this:
|
@natario1 updated the pull request - view changes |
Ok guys, reverted that logic and renamed to availableKeys() . Since this has become something serious, the next design question would be if we should add the set also to So, for example, you have the So, now that we have
What do you think @rogerhu @flovilmart ? I think it can be part of a future PR ... |
Just to clarify, the current implementation (with this PR) of |
If it works, don't change the internal yet :) |
Good enough for now. :). Thanks! |
When are you planning to release next version @rogerhu ? |
Snapshots are released with each merge. That should be good to test no? |
Working also on Bintray access so I can deploy the v14.0.0 version currently on snapshot too. :) |
Nice, no hurry. By the way, we are pushing 1.14.1 to Sonatype, is that a typo? (should be 1.14.0) |
Yeah my fail. :) It's 1.14.1-SNAPSHOT (I'll fix it to 1.14.0) |
@@ -136,7 +136,7 @@ public Integer then(Task<JSONObject> task) throws Exception { | |||
} | |||
for (int i = 0; i < results.length(); ++i) { | |||
JSONObject data = results.getJSONObject(i); | |||
T object = ParseObject.fromJSON(data, resultClassName, state.selectedKeys() == null); | |||
T object = ParseObject.fromJSON(data, resultClassName, ParseDecoder.get(), state.selectedKeys()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@natario1 do you remember why we need to pass in ParseDecoder.get() here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rogerhu it’s just an instance of ParseDecoder.
I had to change the signatures of ParseObject.fromJSON and this seemed legit. ParseDecoder was present in the other signature (used here by ParseDecoder itself, passing this
), so I added it here as well for consistency.
Since ParseDecoder is a singleton we could just drop it from both signatures, but I guess original authors wanted to keep the chance of passing a different decoder. I don’t know.
Adds a availableKeys property to ParseObject.state that includes keys that can be safely accessed. Fixes IllegalStateExceptions thrown when using query.selectKeys() #595 so now there is consistency between complete and non-complete objects.
While it seems to work on my machine, I have no deep understanding of the library. Nothing should be broken though.
Exposing
ParseObject.isDataAvailable(String key)
. Right now there are:ParseObject.has(String key)
: alias to containsKey(key)ParseObject.containsKey(String key):
true if the server sent out data for this keyNeither of these can be used to know if
ParseObject.get*
methods can be called safely. In some caseshas(key)
returns false butget(key)
can be called without throwing. In some othershas(key)
returns false andget(key)
will throw.This PR exposes
isDataAvailable(String key)
for partially fetched objects. When false,get*
methods will throw. So with the PR:ParseObject.containsKey(String key)
: we have some data for this key from the server.ParseObject.isDataAvailable()
: the object is complete, all getters can be called.ParseObject.isDataAvailable(String key)
: the object might not be complete, but you can call get(key) safely.This makes sense to me.
Fixes No support for dotted notation in query.selectKeys() #597 adding support for nested selected keys. If there are some, nested objects will not be considered complete.